package forum.client.controllerlayer;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
import java.util.logging.Logger;
import forum.Settings;
import forum.client.exceptions.*;
import forum.communication.*;

/**
 * This class handles the communication between the client and the server.
 *
 * @author Tomer Heber
 *
 */
public class ClientConnectionController {

	private Socket m_socket;
	private ObjectInputStream in;
	private ObjectOutputStream out;

	private static Logger log = Logger.getLogger(Settings.loggerName);


	public ClientConnectionController(String addr, short port) throws IOException {
		InetAddress ia = InetAddress.getByName(addr);
		connect(ia,port);
	}

	//@Override
	public void run() {
//		BufferedReader prompt = new BufferedReader(new InputStreamReader(System.in));
//		printHelp();
//		while (true) {
//			try {
//			/* Receives a command from the user. */
//			System.out.print("> ");
//			String str = prompt.readLine();
//
//			if (str == null) {
//				break;
//			}
//
//			if (str.equals("disconnect")) {
//				break;
//			}
//
//			if (str.equals("help")) {
//				printHelp();
//				continue;
//			}
//
//			ClientMessage msg;
//			try {
//				/* Handles the command. */
//				msg = handleCommand(str);
//			} catch (BadCommandException e) {
//				log.info(e.getMessage());
//				//e.printStackTrace();
//				System.out.println();
//				continue;
//			}
//			/* send the message to the server. */
//			out.writeObject(msg);
//			/* receive response from the server. */
//			Object o = in.readObject();
//			if (o == null) {
//				log.severe("Lost connection to server.");
//				break;
//			}
//			if (!(o instanceof ServerResponse)) {
//				log.severe("Received an invalid response from server.");
//				break;
//			}
//
//			ServerResponse res = (ServerResponse)o;
//			/* Check if the server has done the command. */
//			if (res.hasExecuted()) {
//				System.out.println("done!");
//			}
//			else {
//				System.out.println("failed!");
//			}
//			/* Print the response from the server */
//			System.out.println(res.getResponse());
//
//		} catch (ClassNotFoundException e) {
//			log.severe("Received an invalid object from the server.");
//			log.severe(e.getMessage());
//			System.out.println("Please try again");
//
//		} catch (IOException e) {
//			log.severe("IOException occured while trying to read/send/write.\n");
//			log.severe(e.getMessage());
//			break;
//		}
//	}
//
//		try {
//			/* Closing all the streams. */
//			prompt.close();
//			closeConnection();
//		} catch (IOException e) {
//			log.severe("IOException while trying to close streams.");
//			//e.printStackTrace();
//		}
	}

//	private void printHelp() {
//		System.out.println(
//				"help menu:" + "\n" +
//				"- help " +  "\n" +
//				"- add_message <message header (no spaces)> <message content>" + "\n" +
//				"- add_reply <message id to reply to> <message header (no spaces)> <message content>" + "\n" +
//				"- modify_message <message id to modify> <new message content>" + "\n" +
//				"- view_forum" + "\n" +
//				"- logoff" + "\n" +
//				"- login <username> <password>" + "\n" +
//				"- register <username> <password> <firstname> <lastname> <e-mail> <gender> <city> <birthday>" + "\n" +
//				"- disconnect" + "\n" +
//				"- search_by_author <username of author> <from> <to>" + "\n" +
//				"- search_by_content <from> <to> <search phrase> " + "\n" +
//				"- promote_moderator <username to promote> "+ "\n" +
//				"- demote_moderator <username to promote>\n"
//		);
//
//	}
//
//	/**
//	 * This method receives commands and creates the proper message to send to the server.
//	 *
//	 * @param str The command to do.
//	 * @return The message to send back to the server.
//	 * @throws BadCommandException This exception is thrown is the command given by the user is invalid.
//	 */
//	private ClientMessage handleCommand(String str) throws BadCommandException {
//		try {
//
//			StringTokenizer st = new StringTokenizer(str);
//
//			String command = st.nextToken();
//			System.out.println("handling command");
//
//			if (command.equals("add_message")) {
//				String header = st.nextToken();
//				return new AddMessageMessage(header, new PostContent(str.substring(command.length()+header.length()+2)));
//			}
//
//			if (command.equals("view_forum")) {
//				return new ViewForumMessage();
//			}
//			if (command.equals("login")) {
//				return new LoginMessage(st.nextToken(),st.nextToken());
//			}
//			if (command.equals("logoff")) {
//				return new LogoffMessage();
//			}
//			if (command.equals("register")) {
//				String username = st.nextToken();
//				String password = st.nextToken();
//				String firstName = st.nextToken();
//				String lastName = st.nextToken();
//				String email = st.nextToken();
//				String gen = st.nextToken();
//
//				Gender gender;
//				if (gen.equals("male")) {
//					gender = Gender.MALE;
//				}
//				else if (gen.equals("female")) {
//					gender = Gender.FEMALE;
//				}
//				else {
//					throw new BadCommandException("The command "+str+" is invalid. gender is not specified.");
//				}
//
//				String city = st.nextToken();
//				Date birthday = new Date(); //try to take the user's inputedS date.
//				return new RegisterMessage(username, password, firstName, lastName, email, gender, city, birthday);
//			}
//
//			if (command.equals("add_reply")) {
//				String messageIdS = st.nextToken();
//				int messageId = Integer.parseInt(messageIdS);
//				String header = st.nextToken();
//				return new AddReplyMessage(messageId, header, new PostContent(str.substring(command.length()+messageIdS.length()+header.length()+2)));
//			}
//			if (command.equals("modify_message")) {
//				String messageIdS = st.nextToken();
//				int messageId = Integer.parseInt(messageIdS);
//				return new ModifyMessageMessage(messageId, new PostContent(str.substring(command.length()+messageIdS.length()+1)));
//			}
//
//			// Search messages.
//			if (command.equals("search_by_author")) {
//				String author = st.nextToken();
//				int from = Integer.parseInt(st.nextToken());
//				int to = Integer.parseInt(st.nextToken());
//				return new SearchByAuthorMessage(author,from,to);
//			}
//
//			if (command.equals("search_by_content")) {
//				String fromString = st.nextToken();
//				String toString = st.nextToken();
//				int from = Integer.parseInt(fromString);
//				int to = Integer.parseInt(toString);
//				String content = str.substring(command.length() + fromString.length() + toString.length() + 3);
//				return new SearchByContentMessage(content,from,to);
//			}
//
//
//			// Admin messages
//			if (command.equals("promote_moderator")) {
//				String toPromote = st.nextToken();
//				return new PromoteModeratorMessage(toPromote);
//			}
//
//			if (command.equals("demote_moderator")) {
//				String toDemote = st.nextToken();
//				return new PromoteModeratorMessage(toDemote);
//			}
//		}
//		catch(Exception e) {
//			throw new BadCommandException(e.getMessage()+"\nThe command "+str+" is invalid!.");
//		}
//
//		throw new BadCommandException("The command "+str+" is unknown.");
//	}

	public void connect(InetAddress addr, short port) throws IOException {
		SocketAddress sa = new InetSocketAddress(addr,port);
		/* Connect to the server */
		m_socket = new Socket();
		m_socket.connect(sa);
		out = new ObjectOutputStream(m_socket.getOutputStream());
		out.flush();
		in = new ObjectInputStream(m_socket.getInputStream());
		log.info("The client is connected to the server.");
	}

	public void closeConnection() throws IOException {
		in.close();
		out.close();
		m_socket.close();
	}


//	public void createConnection(String ip, short port){
//		Thread thread;
//		try {
//			thread = new ClientConnectionController(ip,port);
//			thread.start();
//			thread.join();
//		}catch (IOException e) {
//				System.out.println("Could not connect to server:");
//				System.out.println(e.getMessage());
//				System.out.println("Waiting 15sec untill retry...");
//				try {
//					Thread.sleep(15000);
//				} catch (InterruptedException e1) {
//					log.severe("Could not login. please restart the client.");
//				}
//				createConnection(ip,port);
//
//		}catch (InterruptedException e2) {
//			log.severe("Lost connection to the server. please restart the client.");
//			e2.printStackTrace();
//		}
//	}

	/**
	 * Send a message to the server, and return the response.
	 * This method is synchronous.
	 *
	 * @param msg
	 * @return response string from the server
	 * @throws CommunicationException
	 */
	public ServerResponse sendMsg(ClientMessage msg)
	throws CommunicationException {

		Object o;

		try {
			/* send the message to the server. */
			this.out.writeObject(msg);
		} catch (IOException ex) {
			throw new CommunicationException("Error while writing to socket.");
		}

		try {
			/* receive response from the server. */
			o = this.in.readObject();
		} catch (Exception ex) {
			throw new CommunicationException("Error while reading socket.");
		}

		if (o == null) {
			log.severe("Lost connection to server.");
			throw new CommunicationException("Lost connection to server.");
		}

		if (!(o instanceof ServerResponse)) {
			log.severe("Received an invalid response from server.");
			throw new CommunicationException
			("Received an invalid response from server.");
		}

		return (ServerResponse)o;
	}

}
